package es.greenandroid.shape;

import es.greenandroid.main.R;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.view.View;

public class Circle extends View 
{
	private int radius;
	private int diameter;
	private int diameterMargin;
	private float positionX;
    private float positionY;
	private int color;
    private String text;
    
    /**
     * This constructor version is only needed if you will be instantiating
     * the object manually (not from a layout XML file).
     * 
     * @param Context context
     * @param int radius
     * @param float positionX
     * @param float positionY
     * @param String hexadecimalColor
     * @param String text					
     */
	public Circle (Context context, int radius, float positionX, float positionY, String hexadecimalColor, String text) {
		super(context);
		
		setRadius(radius);
		setDiameter(this.radius);
		setDiameterMargin();
		setPositionX(positionX);
		setPositionY(positionY);
		setColor(Color.parseColor(hexadecimalColor));
		setText(text);
	}

    /**
     * This constructor version is only needed if you will be instantiating
     * the object from a layout XML file. Initializing it with any attributes we 
     * understand. 
     * 
     * These attributes are defined in: /res/values/attrs.xml
     * 
     * @param Context context
     * @param AttributeSet attributes
     */
	public Circle(Context context, AttributeSet attributes) {
		super(context, attributes);
		
		TypedArray attributesList = context.obtainStyledAttributes(attributes, R.styleable.es_greenandroid_shape_Circle);

	     /* NOTE: The getLeft() and the getTop() method represents the 
		  * start point that the onDraw() method will use to start
		  * drawing.
		  */
		
		setRadius(attributesList.getInt(R.styleable.es_greenandroid_shape_Circle_radius, 10));
		setDiameter(this.radius);
		setDiameterMargin();
		setPositionX(getLeft()+this.radius);
		setPositionY(getTop()+this.radius);
		setColor(Color.parseColor(attributesList.getString(R.styleable.es_greenandroid_shape_Circle_color)));
		setText(attributesList.getString(R.styleable.es_greenandroid_shape_Circle_text));
	}

	/**
	 * Determines and sets the rendering size of the component.
	 * 
	 * @param int widthMeasureSpec
	 * @param int heightMeasureSpec
	 */
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    	int measureWidth = measureWidth(widthMeasureSpec);
    	int measureHeight = measureHeight(heightMeasureSpec);
    	
        setMeasuredDimension(measureWidth, measureHeight);
    }
    
    /**
     * Determines the width of this view. If the android:layout_width is set to fill_parent
     * value the specMode will be MeasureSpec.EXACTLY and if it is set to wrap_content value
     * the specMode will be MeasureSpec.AT_MOST.
     * 
     * @param measureSpec 	A measureSpec packed into an int
     * @return The width of the view, honoring constraints from measureSpec
     */
    private int measureWidth(int measureSpec) {
        int result;
        int specMode = MeasureSpec.getMode(measureSpec);
        int specSize = MeasureSpec.getSize(measureSpec);
        
        switch(specMode)
        {
        	case MeasureSpec.EXACTLY: {
        		result = specSize;
        		break;
        	}
        	case MeasureSpec.AT_MOST: {
        		result = radius*2;
        		result = Math.min(result, specSize);
        		break;
        	}
        	default: {
        		result = radius*2;
        	}	
        }

        return result;
    }
	
    /**
     * Determines the height of this view. If the android:layout_height is set to fill_parent
     * value the specMode will be MeasureSpec.EXACTLY and if it is set to wrap_content value
     * the specMode will be MeasureSpec.AT_MOST.
     * @param measureSpec 	A measureSpec packed into an int
     * @return The height of the view, honoring constraints from measureSpec
     */
    private int measureHeight(int measureSpec) {
        int result;
        int specMode = MeasureSpec.getMode(measureSpec);
        int specSize = MeasureSpec.getSize(measureSpec);
        
        switch(specMode)
        {
        	case MeasureSpec.EXACTLY: {
        		result = specSize;
        		break;
        	}
        	case MeasureSpec.AT_MOST: {
        		result = radius*2;
        		result = Math.min(result, specSize);
        		break;
        	}
        	default: {
        		result = radius*2;
        	}	
        }

        return result;
    }
	
    /**
     * Renders the circle and the text, in the correct order.
     * 
     * @param Canvas canvas
     */
	@Override
	protected void onDraw(Canvas canvas) {
		 super.onDraw(canvas);
		 
		 drawCircle(canvas);
		 
		 if(this.text != null) {			 
		 	 drawText(canvas);
		 }
	}
	
	private void drawCircle(Canvas canvas) {
		Paint paintCircle = paintCircle();
		canvas.drawCircle(getPositionX(), getPositionY(), getRadius(), paintCircle);
	}
	
	private Paint paintCircle() {
		Paint paint = new Paint();
		
		stylizeCircle(paint);
		
		return paint;
	}
	
	private void stylizeCircle(Paint paint) {
		paint.setAntiAlias(true);
		paint.setColor(this.color);
	}
	
	private void drawText(Canvas canvas) {
		Paint paintText = paintText();			 
		float textPositionX = getPositionX() - (paintText.measureText(this.text) / 2);
		float textPositionY = getPositionY() + (paintText.getTextSize() / 3);
		canvas.drawText(getText(), textPositionX, textPositionY, paintText);
	}
	
	private Paint paintText() {
		Paint paint = new Paint();
		
		if(isTextWidthGreaterThanDiameter(paint)) {	
			decreaseTextWidth(paint);
		}  				
		else {
			increaseTextWidth(paint);
		}
		
		while(isTextSizeGreaterThanDiameter(paint)){
			decreaseTextSize(paint);
		}
		
		stylizeText(paint);
		
		return paint;
	}
	
	private boolean isTextWidthGreaterThanDiameter(Paint paint) {
		return (paint.measureText(this.text) > getDiameter()-getDiameterMargin());
	}
	
	private void decreaseTextWidth(Paint paint) {
		while(isTextWidthGreaterThanDiameter(paint)) {
			decreaseTextSize(paint);
		}
	}
	
	private void decreaseTextSize(Paint paint) {
		paint.setTextSize(paint.getTextSize()-1);
	}
	
	private void increaseTextWidth(Paint paint) {	
		while(isTextWidthLessThanDiameter(paint)) {
			increaseTextSize(paint);
		}
	}
	
	private boolean isTextWidthLessThanDiameter(Paint paint) {
		return (paint.measureText(this.text) < getDiameter()-getDiameterMargin());
	}
	
	private void increaseTextSize(Paint paint) {
		paint.setTextSize(paint.getTextSize()+1);
	}
	
	private boolean isTextSizeGreaterThanDiameter(Paint paint) {
		return (paint.getTextSize() > getDiameter()-getDiameterMargin());
	}
	
	private void stylizeText(Paint paint) {
		paint.setAntiAlias(true);
	}
	
	public int getRadius() {
		return this.radius;
	}

	public int getDiameter() {
		return this.diameter;
	}
	
	public int getDiameterMargin() {
		return this.diameterMargin;
	}
	
	public float getPositionX() {
		return this.positionX;
	}
	
	public float getPositionY() {
		return this.positionY;
	}
	
	public int getColor() {
		return this.color;
	}

	public String getText() {
		return this.text;
	}

	public void setRadius(int radius) {
		this.radius = radius;
		setDiameter(this.radius);
	}

	public void setPositionX(float positionX) {
		this.positionX = positionX;
	}

	public void setPositionY(float positionY) {
		this.positionY = positionY;
	}

	public void setColor(int color) {
		this.color = color;
	}

	public void setText(String text) {
		this.text = text;
	}
	
	private void setDiameter(int radius) {
		this.diameter = radius * 2;
	}
	private void setDiameterMargin() {
		this.diameterMargin = getDiameter() / 4;
	}
}
